package com.bolins.examples.subspringside.filter;

import java.awt.image.BufferedImage;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import com.octo.captcha.service.CaptchaService;
import com.octo.captcha.service.CaptchaServiceException;

/**
 * 与SpringSecurity整合的Jcaptcha验证码
 * @author liuys
 *
 */
public class JcaptchaFilter implements Filter {
	private static final Logger logger = LoggerFactory.getLogger(JcaptchaFilter.class);

	//定义Web.xml中的参数名
	private static final String CAPTCHA_SERVICE_ID = "captchaService";
	private static final String VALIDATE_FILTER_URL = "validateFilterPath";
	private static final String AUTO_PASS = "autoPass";
	private static final String PARAM_CAPTCHA_NAME = "captchaParamName";
	private static final String FAIL_VALIDATE_URL = "failValidatePath";

	//如Web.xml中未定义参数时将用默认的值
	private static final String DEFAULT_CAPTCHA_SERVICE_ID = "captchaServiceII";
	private static final String DEFAULT_VALIDATE_FILTER_URL = "/j_spring_security_check";
	private static final String DEFAULT_PARAM_CAPTCHA_NAME = "j_captcha";
	private static final String DEFAULT_FAIL_VALIDATE_URL = "/login.action?error=1";

	//参数对应的值
	private String captchaServiceId = DEFAULT_CAPTCHA_SERVICE_ID;
	private String validateFilterUrl = DEFAULT_VALIDATE_FILTER_URL;
	private String captchaParamName = DEFAULT_PARAM_CAPTCHA_NAME;
	private String failValidateUrl = DEFAULT_FAIL_VALIDATE_URL;
	private String autoPass;

	private CaptchaService captchaService;

	@Override
	public void destroy() {
		captchaService = null;
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
			ServletException {
		HttpServletRequest req = (HttpServletRequest) request;
		HttpServletResponse resp = (HttpServletResponse) response;
		String servletPath = req.getServletPath();

		if (StringUtils.startsWith(servletPath, validateFilterUrl)) {
			if (validateCaptcha(req)) {
				chain.doFilter(req, resp);
			} else {
				resp.sendRedirect(req.getContextPath() + failValidateUrl);
			}
		} else {
			generatorCaptchaImage(req, resp);
		}
	}

	protected void generatorCaptchaImage(HttpServletRequest req, HttpServletResponse resp) throws IOException {
		resp.setDateHeader("Expires", 0);
		resp.addHeader("Pragma", "no-cache");
		resp.setHeader("Cache-Control", "no-cache");
		resp.setContentType("image/jpeg");

		ServletOutputStream output = resp.getOutputStream();

		try {
			String captchaId = req.getSession().getId();
			BufferedImage image = (BufferedImage) captchaService.getChallengeForID(captchaId, req.getLocale());
			ImageIO.write(image, "jpg", output);
			output.flush();
		} catch (CaptchaServiceException e) {
			logger.error(e.getMessage(), e);
		} finally {
			output.close();
		}
	}

	protected boolean validateCaptcha(HttpServletRequest req) {
		String captchaId = req.getSession().getId();
		String captchaChallonge = req.getParameter(captchaParamName);

		if (StringUtils.isNotBlank(captchaChallonge) && StringUtils.equals(captchaChallonge, autoPass)) {
			return true;
		}

		return captchaService.validateResponseForID(captchaId, captchaChallonge);
	}

	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		this.initParameters(filterConfig);
		this.initCaptchaService(filterConfig);
	}

	protected void initParameters(final FilterConfig filterConfig) {
		if (StringUtils.isNotBlank(filterConfig.getInitParameter(CAPTCHA_SERVICE_ID))) {
			captchaServiceId = filterConfig.getInitParameter(CAPTCHA_SERVICE_ID);
		}

		if (StringUtils.isNotBlank(filterConfig.getInitParameter(VALIDATE_FILTER_URL))) {
			validateFilterUrl = filterConfig.getInitParameter(VALIDATE_FILTER_URL);
		}

		if (StringUtils.isNotBlank(filterConfig.getInitParameter(AUTO_PASS))) {
			autoPass = filterConfig.getInitParameter(AUTO_PASS);
		}

		if (StringUtils.isNotBlank(filterConfig.getInitParameter(PARAM_CAPTCHA_NAME))) {
			captchaParamName = filterConfig.getInitParameter(PARAM_CAPTCHA_NAME);
		}

		if (StringUtils.isNotBlank(filterConfig.getInitParameter(FAIL_VALIDATE_URL))) {
			failValidateUrl = filterConfig.getInitParameter(FAIL_VALIDATE_URL);
		}
	}

	protected void initCaptchaService(final FilterConfig filterConfig) {
		ApplicationContext ac = WebApplicationContextUtils.getWebApplicationContext(filterConfig.getServletContext());
		captchaService = (CaptchaService) ac.getBean(captchaServiceId);
	}

}
